// i.d(t, { i: () => FloorsData })

import Data from "../object/Data"
import Floor from "../object/Floor"
import { createSubscription, Subscription } from "../utils/Subscription"
import ObservableMap, { createMap } from "../observer/observable.map"
import { InvalidFloorException } from "../exception/floor.exception"

export default class FloorsData extends Data {
  floors: ObservableMap<Floor>
  constructor(e: any) {
    super()
    this.name = "floors"
    this.floors = createMap(e)
  }
  onNameChange(e) {
    let t: Subscription[] = []
    return createSubscription(
      () => {
        this.getOrderedValues().forEach((i, n) => {
          t.push(i.onPropertyChanged("name", e))
        })
      },
      () => {
        t.forEach(e => e.cancel()), (t = [])
      },
      !0
    )
  }
  getFloor(e: string) {
    if (!this.floors.has(e)) throw new InvalidFloorException()
    return this.floors.get(e)
  }
  hasFloor(e: string) {
    return this.floors.has(e)
  }
  getFloorAtIndex(e: number) {
    return this.floors.values.find(t => t.index === e)
  }
  getFloorByMeshGroup(e: number) {
    return this.floors.values.find(t => t.meshGroup === e)
  }
  mapMeshesToFloors(e: { boundingBox: THREE.Box3; meshGroup: number; parentMeshGroup: any }[]) {
    const t = new Map()
    for (const t of e) {
      const { meshGroup, boundingBox } = t
      let n = this.getFloorByMeshGroup(meshGroup)
      const r = n?.id || String(meshGroup)
      n ||
        ((n = new Floor({
          id: r,
          meshGroup: meshGroup,
          index: meshGroup,
          runtimeOnly: !0
        })),
        this.floors.set(n.id, n)),
        n.setBounds(boundingBox)
    }
    this.getOrderedValues().forEach((e, i) => {
      e.index = i
      t.set(e.meshGroup, e.id)
    })
    return t
  }
  getFloorCount() {
    return this.floors.length
  }
  getCollection() {
    return this.floors
  }
  iterate(e: (value: Floor, index: number, array: Floor[]) => void) {
    return this.getOrderedValues().forEach(e)
  }
  getTopFloor() {
    const e = this.getOrderedValues()
    if (!e.length) throw new InvalidFloorException("Cannot get top floor, model has no floors")
    return e[e.length - 1]
  }
  getBottomFloor() {
    const e = this.getOrderedValues()
    if (!e.length) throw new InvalidFloorException("Cannot get bottom floor, model has no floors")
    return e[0]
  }
  getFloorAtHeight(e: number, t = !1) {
    const i = this.getOrderedValues()
    t && i.reverse()
    for (const t of i) {
      const i = t.boundingBox
      if (i.min.y <= e && e <= i.max.y) return t
    }
    const n = this.getBottomFloor()
    return e < n.boundingBox.min.y ? n : this.getTopFloor()
  }
  getClosestFloorAtHeight(e: number) {
    let t: Floor
    let i = 1 / 0
    for (const n of this.floors) {
      const s = Math.abs(n.medianSweepHeight() - e)
      i > s && ((i = s), (t = n))
    }
    return t
  }
  rename(e: string, name: string) {
    if (!this.floors.has(e)) return
    const i = this.floors.get(e)
    i.name = name
    i.commit()
  }
  getOrderedValues() {
    return this.floors.values.sort((e, t) => e.index - t.index)
  }
}
